#include "mesh.h"

using namespace G3;

//==============================================================================
// Constructors & Destructors
//==============================================================================
G3::TRIANGLE_STRIPI::TRIANGLE_STRIPI () {
	num_indices = 0;
	indices = NULL;
}

G3::TRIANGLE_FANI::TRIANGLE_FANI () {
	num_indices = 0;
	indices = NULL;
}

G3::MESH_NODE::MESH_NODE () {
	type = OBJ_NODE_MESH;

	material = NULL;
	vertices = NULL;
	tex_coords = NULL;
	tri_strips = NULL;
	tri_fans = NULL;

	num_tri_strips = 0;
	num_tri_fans = 0;
	num_verts = 0;
	num_tex_coords = 0;
}

G3::MESH_NODE::MESH_NODE (shared_ptr<OBJECT_NODE> nodeParent):
	ANIM_NODE (nodeParent, G3::OBJ_NODE_MESH)
{
	type = OBJ_NODE_MESH;

	material = NULL;
	vertices = NULL;
	tex_coords = NULL;
	tri_strips = NULL;
	tri_fans = NULL;

	num_tri_strips = 0;
	num_tri_fans = 0;
	num_verts = 0;
	num_tex_coords = 0;
}

G3::TRIANGLE_STRIPI::~TRIANGLE_STRIPI () {
    delete [] indices;
	indices = NULL;
}

G3::TRIANGLE_FANI::~TRIANGLE_FANI () {
    delete [] indices;
	indices = NULL;
}

G3::MESH_NODE::~MESH_NODE () {
	Clear ();
}

//==============================================================================
// Allocates memory for a trianglestrip
//
//		uint numIndices				The number of indices to allocate memory for
//==============================================================================
void G3::TRIANGLE_STRIPI::Create (uint numIndices) {
	if (this->num_indices != 0) {
		this->num_indices = 0;
		delete [] indices;
		indices = NULL;
	}

	this->num_indices = numIndices;
	indices = new uint [numIndices];
}

//==============================================================================
// Allocates memory for a trianglefan
//
//		uint numIndices				The number of indices to allocate memory for
//==============================================================================
void G3::TRIANGLE_FANI::Create (uint numIndices) {
	if (this->num_indices != 0) {
		this->num_indices = 0;
		delete [] indices;
		indices = NULL;
	}

	this->num_indices = numIndices;
	indices = new uint [numIndices];
}

//==============================================================================
// Clears the mesh
//==============================================================================
void G3::MESH_NODE::Clear () {
	if (vertices != NULL) {
		delete [] vertices;
		vertices = NULL;
	}

	if (tex_coords != NULL) {
		delete [] tex_coords;
		tex_coords = NULL;
	}

	if (tri_strips != NULL) {
		delete [] tri_strips;
		tri_strips = NULL;
	}

	if (tri_fans != NULL) {
		delete [] tri_fans;
		tri_fans = NULL;
	}
}

//==============================================================================
// Creates a mesh and allocates memory for vertices, tex_coords, Strip, Fans
//
//		uint NumVerts				Number of vertices
//
//		uint NumTexCoords			Number of texture coordinates
//
//		uint NumStrips				Number of triangle strips
//
//		uint NumFans				Number of triangle fans
//==============================================================================
bool G3::MESH_NODE::Create (uint numVerts, uint numTexCoords, uint numStrips, uint numFans) {
	num_verts = numVerts;
	num_tex_coords = numTexCoords;
	num_tri_strips = numStrips;
	num_tri_fans = numFans;

	if (numVerts > 0) {
		vertices = new VEC3F [numVerts];

		if (vertices == NULL)
			return false;
	}

	if (numTexCoords > 0) {
		tex_coords = new VEC2F [numTexCoords];

		if (tex_coords == NULL)
			return false;
	}

	if (numFans > 0) {
		tri_fans = new TRIANGLE_FANI [numFans];

		if (tri_fans == NULL)
			return false;
	}

	if (numStrips > 0) {
		tri_strips = new TRIANGLE_STRIPI [numStrips];

		if (tri_strips == NULL)
			return false;
	}

	return true;
}

//==============================================================================
// Sets the mesh material
//
//		MATERIAL *Material			Pointer to the material to use
//==============================================================================
void G3::MESH_NODE::SetMaterial (MATERIAL *aMaterial) {
	this->material = aMaterial;
}

//==============================================================================
// Sets mesh vertices
//
//		VEC3F *Verts				Pointer to the array vertices
//
//	NOTE:: It copies num_verts vertices from the array
//==============================================================================
void G3::MESH_NODE::SetVertices (VEC3F *verts) {
	if (verts != NULL) {
		memcpy (vertices, verts, sizeof (VEC3F) * num_verts);
	}
}

//==============================================================================
// Sets mesh texture coordinates
//
//		VEC2F *TexCoords			Pointer to the array texture coordinates
//
//	NOTE:: It copies num_tex_coords coordinates from the array
//		   The array should be ordered the same way the vertices are
//==============================================================================
void G3::MESH_NODE::SetTexCoords (VEC2F *texCoords) {
	if (texCoords != NULL) {
		memcpy (tex_coords, texCoords, sizeof (VEC2F) * num_tex_coords);
	}
}

//==============================================================================
// Sets mesh indices for a triangle strip
//
//		uint stripID				The index of the strip to set
//
//		uint numIndices				Number of indices to set
//
//		uint aIndices				Pointer to an array of indices
//
//	NOTE:: The same indices count for vertices and texture coordinates
//==============================================================================
void G3::MESH_NODE::SetStrip (uint stripID, uint numIndices, uint *aIndices) {
	if (stripID >= 0 && stripID < num_tri_strips && tri_strips != NULL) {
		tri_strips [stripID].Create (numIndices);
		memcpy (tri_strips [stripID].indices, aIndices, sizeof (uint) * numIndices);
	}
}

//==============================================================================
// Sets mesh indices for a triangle fan
//
//		uint fanID					The index of the fan to set
//
//		uint numIndices				Number of indices to set
//
//		uint aIndices				Pointer to an array of indices
//
//	NOTE:: The same indices count for vertices and texture coordinates
//==============================================================================
void G3::MESH_NODE::SetFan (uint fanID, uint numIndices, uint *aIndices) {
	if (fanID >= 0 && fanID < num_tri_fans && tri_fans != NULL) {
		tri_fans [fanID].Create (numIndices);
		memcpy (tri_fans [fanID].indices, aIndices, sizeof (uint) * numIndices);
	}
}

//==============================================================================
// Creates a cube with edge length Side
//==============================================================================
void G3::MESH_NODE::CreateCube (float side) {
	float A = side / 2.0f;
	VEC3F verts [16];
	VEC2F texCoords [16];
	uint indices [18];

	Clear ();
	Create (16, 16, 1, 2);
	name = "Cube";

	verts [0] = VEC3F (-A, A,-A);
	verts [1] = VEC3F (-A,-A,-A);
	verts [2] = VEC3F ( A, A,-A);
	verts [3] = VEC3F ( A,-A,-A);
	verts [4] = VEC3F ( A, A, A);
	verts [5] = VEC3F ( A,-A, A);
	verts [6] = VEC3F (-A, A, A);
	verts [7] = VEC3F (-A,-A, A);
	verts [8] = VEC3F (-A, A,-A);
	verts [9] = VEC3F ( A, A,-A);
	verts [10] = VEC3F ( A, A, A);
	verts [11] = VEC3F (-A, A, A);
	verts [12] = VEC3F (-A,-A,-A);
	verts [13] = VEC3F (-A,-A, A);
	verts [14] = VEC3F ( A,-A, A);
	verts [15] = VEC3F ( A,-A,-A);

	texCoords [0] = VEC2F (0, 0);
	texCoords [1] = VEC2F (0, 1);
	texCoords [2] = VEC2F (1, 0);
	texCoords [3] = VEC2F (1, 1);
	texCoords [4] = VEC2F (0, 0);
	texCoords [5] = VEC2F (0, 1);
	texCoords [6] = VEC2F (1, 0);
	texCoords [7] = VEC2F (1, 1);
	texCoords [8] = VEC2F (0, 0);
	texCoords [9] = VEC2F (1, 0);
	texCoords [10] = VEC2F (1, 1);
	texCoords [11] = VEC2F (0, 1);
	texCoords [12] = VEC2F (0, 0);
	texCoords [13] = VEC2F (1, 0);
	texCoords [14] = VEC2F (1, 1);
	texCoords [15] = VEC2F (0, 1);

	indices [0] = 0; indices [10] = 8;
	indices [1] = 1; indices [11] = 9;
	indices [2] = 2; indices [12] = 10;
	indices [3] = 3; indices [13] = 11;
	indices [4] = 4; indices [14] = 12;
	indices [5] = 5; indices [15] = 13;
	indices [6] = 6; indices [16] = 14;
	indices [7] = 7; indices [17] = 15;
	indices [8] = 0;
	indices [9] = 1;

	SetVertices (verts);
	SetTexCoords (texCoords);

	SetStrip (0, 10, indices);
	SetFan (0, 4, &indices [10]);
	SetFan (1, 4, &indices [14]);
}

void G3::MESH_NODE::CreatePlane (float side) {
	float A = side / 2.0f;
	VEC3F verts [4];
	VEC2F texCoords [4];
	uint indices [4];

	Clear ();

	Create (4, 4, 1, 0);
	name = "Plane";

	verts [0] = VEC3F (-A, 0,-A);
	verts [1] = VEC3F ( A, 0,-A);
	verts [2] = VEC3F (-A, 0, A);
	verts [3] = VEC3F ( A, 0, A);

	texCoords [0] = VEC2F (0, 0);
	texCoords [1] = VEC2F (1, 0);
	texCoords [2] = VEC2F (0, 1);
	texCoords [3] = VEC2F (1, 1);

	indices [0] = 0;
	indices [1] = 1;
	indices [2] = 2;
	indices [3] = 3;

	SetVertices (verts);
	SetTexCoords (texCoords);

	SetStrip (0, 4, indices);
}

//==============================================================================
// Multiplies WorldMatrix with ModelMatrix and applies it to OpenGL renderer
//==============================================================================
void G3::MESH_NODE::glApplyMatrix () {
	MATRIX4x4F M;
	M = world_matrix * model_matrix;
	glMultMatrixf (M.m);
}

//==============================================================================
// Renders the mesh
//
//		float ElapsedTime			Time elapsed since the last call
//==============================================================================
void G3::MESH_NODE::Render (float elapsedTime) {
	int i = 0;

	glPushMatrix ();

	glApplyMatrix ();

	if (material != NULL)
		material->glApply ();

	glEnableClientState (GL_VERTEX_ARRAY);
	glEnableClientState (GL_TEXTURE_COORD_ARRAY);

		glTexCoordPointer (2, GL_FLOAT, 0, tex_coords);
		glVertexPointer (3, GL_FLOAT, 0, vertices);

		for (i=0; i<(int)num_tri_strips; i++) {
			glDrawElements (GL_TRIANGLE_STRIP, tri_strips [i].num_indices, GL_UNSIGNED_INT, (void *) tri_strips [i].indices);
		}

		for (i=0; i<(int)num_tri_fans; i++) {
			glDrawElements (GL_TRIANGLE_FAN, tri_fans [i].num_indices, GL_UNSIGNED_INT, (void *) tri_fans [i].indices);
		}

	glDisableClientState (GL_TEXTURE_COORD_ARRAY);
	glDisableClientState (GL_VERTEX_ARRAY);

	glPopMatrix ();

	for (i=0; i<(int)children.size (); i++) {
		if (children [i] != NULL)
			children [i]->Render (elapsedTime);
	}

	if (material != NULL)
        material->glUnApply ();
}

//==============================================================================
// Updates the mesh
//
//		float ElapsedTime			Time elapsed since the last call
//==============================================================================
void G3::MESH_NODE::Update (float elapsedTime) {
	int i = 0;

	// Update controllers
	for (i=0; i<(int)controllers.size (); i++) {
		if (controllers [i]) {
			controllers [i]->Update (elapsedTime);
			// Apply transformation controllers
			if (controllers [i]->GetType () == CT_ROTATION)
				model_matrix = model_matrix * ((ROTATION_CTRL *) controllers [i])->matrix;
			else if (controllers [i]->GetType () == CT_TRANSLATION)
				model_matrix = model_matrix * ((TRANSLATION_CTRL *) controllers [i])->matrix;
			else if (controllers [i]->GetType () == CT_SCALE)
				model_matrix = model_matrix * ((SCALE_CTRL *) controllers [i])->matrix;
		}
	}

	// Update children
	for (i=0; i<(int)children.size (); i++) {
		if (children [i] != NULL)
			children [i]->Update (elapsedTime);
	}
}
